/*
REWRITTEN FROM SCRATCH BY XINEF, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "halls_of_lightning.h"
#include "SpellInfo.h"

enum VolkahnSpells
{
	// Volkhan
	SPELL_HEAT_N						= 52387,
	SPELL_HEAT_H						= 59528,
	SPELL_SHATTERING_STOMP_N			= 52237,
	SPELL_SHATTERING_STOMP_H			= 59529,
	SPELL_TEMPER						= 52238,
	SPELL_SUMMON_MOLTEN_GOLEM			= 52405,

	//Molten Golem
	SPELL_BLAST_WAVE					= 23113,
	SPELL_IMMOLATION_STRIKE_N			= 52433,
	SPELL_IMMOLATION_STRIKE_H			= 59530,
	SPELL_SHATTER_N						= 52429,
	SPELL_SHATTER_H						= 59527,
};

enum VolkhanOther
{
	// NPCs
	NPC_VOLKHAN_ANVIL					= 28823,
	NPC_MOLTEN_GOLEM					= 28695,
	NPC_BRITTLE_GOLEM					= 28681,

	// Misc
	ACTION_SHATTER						= 1,
	ACTION_DESTROYED					= 2,

	// Point
	POINT_ANVIL							= 1,
};

enum VolkhanEvents
{
	// Volkhan
	EVENT_HEAT							= 1,
	EVENT_CHECK_HEALTH					= 2,
	EVENT_SHATTER						= 3,
	EVENT_POSITION						= 4,
	EVENT_MOVE_TO_ANVIL					= 5,

	// Molten Golem
	EVENT_BLAST							= 11,
	EVENT_IMMOLATION					= 12,
};

enum Yells
{
    SAY_AGGRO                               = 0,
    SAY_FORGE                               = 1,
    SAY_STOMP                               = 2,
    SAY_SLAY                                = 3,
    SAY_DEATH                               = 4,
    EMOTE_TO_ANVIL                          = 5,
    EMOTE_SHATTER                           = 6,
};

class boss_volkhan : public CreatureScript
{
public:
    boss_volkhan() : CreatureScript("boss_volkhan") { }

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_volkhanAI (creature);
    }

	struct boss_volkhanAI : public ScriptedAI
	{
		boss_volkhanAI(Creature* creature) : ScriptedAI(creature), summons(creature)
		{
			m_pInstance = creature->GetInstanceScript();
		}

		InstanceScript* m_pInstance;
		EventMap events;
		SummonList summons;
		uint8 HealthCheck;
		float x, y, z;
		uint8 PointID;
		uint8 ShatteredCount;

		void Reset()
		{
			x = y = z = PointID = ShatteredCount = 0;
			HealthCheck = 100;
			events.Reset();
			summons.DespawnAll();
			me->SetSpeed(MOVE_RUN, 1.2f,true);
			me->SetReactState(REACT_AGGRESSIVE);

			if (m_pInstance)
			{
				m_pInstance->SetData(TYPE_VOLKHAN, NOT_STARTED);
				m_pInstance->SetData(DATA_VOLKHAN_ACHIEVEMENT, true);
			}
		}

		void EnterCombat(Unit*)
		{
			me->SetInCombatWithZone();
			Talk(SAY_AGGRO);

			if (m_pInstance)
				m_pInstance->SetData(TYPE_VOLKHAN, IN_PROGRESS);

			ScheduleEvents(false);
		}

		void JustDied(Unit*)
		{
			Talk(SAY_DEATH);

			summons.DespawnAll();

			if (m_pInstance)
				m_pInstance->SetData(TYPE_VOLKHAN, DONE);
		}

		void GetNextPos()
		{
			if (me->GetPositionY() < -180)
			{
				if (me->GetPositionX() > 1330)
					x = 1355;
				else 
					x = 1308;

				y = -178;
				z = 52.5f;
			}
			else if (me->GetPositionY() < -145)
			{
				if (me->GetPositionX() > 1330)
					x = 1355;
				else 
					x = 1308;

				y = -137;
				z = 52.5f;
			}
			else if (me->GetPositionY() < -130)
			{
				if (me->GetPositionX() > 1330)
					x = 1343;
				else 
					x = 1320;

				y = -123;
				z = 56.7f;
			}
			else
			{
				PointID = POINT_ANVIL;
				x = 1327;
				y = -96;
				z = 56.7f;
			}
		}

		void KilledUnit(Unit* victim)
		{
			if (victim->GetTypeId() != TYPEID_PLAYER)
				return;

			Talk(SAY_SLAY);
		}

		void ScheduleEvents(bool anvil)
		{
			events.SetPhase(1);
			events.RescheduleEvent(EVENT_HEAT, 8000, 0, 1);
			events.RescheduleEvent(EVENT_SHATTER, 10000, 0, 1);
			events.RescheduleEvent(EVENT_CHECK_HEALTH, anvil ? 1000 : 6000, 0, 1);
			events.RescheduleEvent(EVENT_POSITION, 4000, 0, 1);
		}

		void JustSummoned(Creature* summon)
		{
			summons.Summon(summon);
			if (summon->GetEntry() == NPC_MOLTEN_GOLEM)
			{
				summon->setFaction(me->getFaction());

				if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
					summon->AI()->AttackStart(target);
			}
		}

		void DoAction(int32 param)
		{
			if (param == ACTION_DESTROYED)
			{
				ShatteredCount++;
				if (ShatteredCount > 4)
					m_pInstance->SetData(DATA_VOLKHAN_ACHIEVEMENT, false);
			}
		}

		void MovementInform(uint32 type, uint32 id)
		{
			if (type != POINT_MOTION_TYPE)
				return;

			if (id == POINT_ANVIL)
			{
				me->SetSpeed(MOVE_RUN, 1.2f,true);
				me->SetReactState(REACT_AGGRESSIVE);
				me->CastSpell(me, SPELL_TEMPER, false);
				PointID = 0;
				ScheduleEvents(true);

				// update orientation at server
				me->SetOrientation(2.19f);

				// and client
				WorldPacket data;
				me->BuildHeartBeatMsg(&data);
				me->SendMessageToSet(&data, false);
				me->SetControlled(true, UNIT_STATE_ROOT);
			}
			else
				events.ScheduleEvent(EVENT_MOVE_TO_ANVIL, 0, 0, 2);
		}

		void SpellHitTarget(Unit* /*who*/, const SpellInfo *spellInfo)
		{
			if (spellInfo->Id == SPELL_TEMPER)
			{
				me->CastSpell(me, SPELL_SUMMON_MOLTEN_GOLEM, true);
				me->CastSpell(me, SPELL_SUMMON_MOLTEN_GOLEM, true);
				me->GetMotionMaster()->MoveChase(me->GetVictim());
				me->SetControlled(false, UNIT_STATE_ROOT);
			}
		}

		void GoToAnvil()
		{
			events.SetPhase(2);
			HealthCheck -= 20;
			me->SetSpeed(MOVE_RUN, 4.0f,true);
			me->SetReactState(REACT_PASSIVE);

			Talk(SAY_FORGE);

			if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
				me->GetMotionMaster()->MovementExpired();

			events.ScheduleEvent(EVENT_MOVE_TO_ANVIL, 0, 0, 2);
		}

		void UpdateAI(uint32 diff)
		{
			//Return since we have no target
			if (!UpdateVictim())
				return;

			events.Update(diff);

			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (events.GetEvent())
			{
				case EVENT_HEAT:
					me->CastSpell(me, me->GetMap()->IsHeroic() ? SPELL_HEAT_H : SPELL_HEAT_N, true);
					events.RepeatEvent(8000);
					break;
				case EVENT_CHECK_HEALTH:
					if (HealthBelowPct(HealthCheck))
						GoToAnvil();

					events.RepeatEvent(1000);
					return;
				case EVENT_SHATTER:
				{
					events.RepeatEvent(10000);
					summons.DoAction(ACTION_SHATTER);
					break;
				}
				case EVENT_MOVE_TO_ANVIL:
					GetNextPos();
					me->GetMotionMaster()->MovePoint(PointID, x, y, z);
					events.PopEvent();
					return;
				case EVENT_POSITION:
					if (me->GetDistance(1331.9f, -106, 56) > 95)
						EnterEvadeMode();
					else
						events.RepeatEvent(4000);
				
					return;
			}
			
			DoMeleeAttackIfReady();
		}
	};
};

class npc_molten_golem : public CreatureScript
{
public:
    npc_molten_golem() : CreatureScript("npc_molten_golem") { }

    CreatureAI* GetAI(Creature* creature) const
    {
        return new npc_molten_golemAI (creature);
    }

	struct npc_molten_golemAI : public ScriptedAI
	{
		npc_molten_golemAI(Creature* creature) : ScriptedAI(creature)
		{
			m_pInstance = creature->GetInstanceScript();
		}

		EventMap events;
		InstanceScript* m_pInstance;

		void Reset()
		{
			events.Reset();
			events.ScheduleEvent(EVENT_BLAST, 7000);
			events.ScheduleEvent(EVENT_IMMOLATION, 3000);
		}

		void DamageTaken(Unit*, uint32 &uiDamage, DamageEffectType, SpellSchoolMask)
		{
			if (me->GetEntry() == NPC_BRITTLE_GOLEM)
			{
				uiDamage = 0;
				return;
			}

			if (uiDamage >= me->GetHealth())
			{
				me->UpdateEntry(NPC_BRITTLE_GOLEM, 0, false);
				me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
				me->SetHealth(me->GetMaxHealth());
				me->RemoveAllAuras();
				me->AttackStop();
				uiDamage = 0;

				if (me->IsNonMeleeSpellCast(false))
					me->InterruptNonMeleeSpells(false);

				me->SetControlled(true, UNIT_STATE_STUNNED);
			}
		}

		void DoAction(int32 param)
		{
			if (me->GetEntry() == NPC_BRITTLE_GOLEM && param == ACTION_SHATTER)
			{
				if (Creature* volkhan = ObjectAccessor::GetCreature(*me, m_pInstance->GetData64(TYPE_VOLKHAN)))
					volkhan->AI()->DoAction(ACTION_DESTROYED);

				me->CastSpell(me, me->GetMap()->IsHeroic() ? SPELL_SHATTER_H : SPELL_SHATTER_N, true);
				me->DespawnOrUnsummon(500);
			}
		}

		void UpdateAI(uint32 diff)
		{
			//Return since we have no target or if we are frozen
			if (!UpdateVictim() || me->GetEntry() == NPC_BRITTLE_GOLEM)
				return;

			events.Update(diff);

			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (events.GetEvent())
			{
				case EVENT_BLAST:
					me->CastSpell(me, SPELL_BLAST_WAVE, false);
					events.RepeatEvent(14000);
					break;
				case EVENT_IMMOLATION:
					me->CastSpell(me->GetVictim(), me->GetMap()->IsHeroic() ? SPELL_IMMOLATION_STRIKE_H : SPELL_IMMOLATION_STRIKE_N, false);
					events.RepeatEvent(5000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum monumentSpells
{
	SPELL_FREEZE_ANIM			= 16245,
	SPELL_AWAKEN				= 52875,

	SPELL_PIERCING_HOWL			= 23600,
	SPELL_PENETRATING_STRIKE	= 52890,
	SPELL_FRIGHTENING_SHOUT		= 19134,
	SPELL_BLADE_TURNING_N		= 52891,
	SPELL_BLADE_TURNING_H		= 59173,

	SPELL_DEADLY_THROW_N		= 52885,
	SPELL_DEADLY_THROW_H		= 59180,
	SPELL_DEFLECTION_N			= 52879,
	SPELL_DEFLECTION_H			= 59181,
	SPELL_THROW_N				= 52904,
	SPELL_THROW_H				= 59179,
};

enum monumentEvents
{
	EVENT_PIERCING_HOWL			= 1,
	EVENT_PENETRATING_STRIKE	= 2,
	EVENT_FRIGHTENING_SHOUT		= 3,
	EVENT_BLADE_TURNING			= 4,

	EVENT_DEADLY_THROW			= 11,
	EVENT_DEFLECTION			= 12,
	EVENT_THROW					= 13,

	EVENT_UNFREEZE				= 20,
};

class npc_hol_monument : public CreatureScript
{
public:
	npc_hol_monument() : CreatureScript("npc_hol_monument") {}

	CreatureAI* GetAI(Creature* creature) const
	{
		return new npc_hol_monumentAI(creature);
	}

	struct npc_hol_monumentAI : public ScriptedAI
	{
		npc_hol_monumentAI(Creature* creature) : ScriptedAI(creature)
		{
			_attackGUID = 0;
			_isActive = urand(0,1);
			me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
			me->CastSpell(me, SPELL_FREEZE_ANIM, true);
		}

		EventMap events;
		bool _isActive;
		uint64 _attackGUID;

		void Reset()
		{
		}

		void MoveInLineOfSight(Unit* who)
		{
			if (_attackGUID)
				ScriptedAI::MoveInLineOfSight(who);
			else if (_isActive && who->GetTypeId() == TYPEID_PLAYER)
			{
				if ((who->GetPositionX() < me->GetPositionX() || who->GetPositionY() < -220.0f) && me->GetDistance2d(who) < 40)
				{
					_isActive = false;
					_attackGUID = who->GetGUID();
					events.Reset();
					events.RescheduleEvent(EVENT_UNFREEZE, 5000);
				}
			}
		}

		void EnterCombat(Unit*)
		{
			events.Reset();
			if (me->GetEntry() == 28961) // NPC_TITANIUM_SIEGEBREAKER
			{
				events.ScheduleEvent(EVENT_PIERCING_HOWL, 10000+rand()%15000);
				events.ScheduleEvent(EVENT_PENETRATING_STRIKE, 5000+rand()%5000);
				events.ScheduleEvent(EVENT_FRIGHTENING_SHOUT, 20000+rand()%8000);
				events.ScheduleEvent(EVENT_BLADE_TURNING, 12000);
			}
			else
			{
				events.ScheduleEvent(EVENT_THROW, 10000+rand()%15000);
				events.ScheduleEvent(EVENT_DEADLY_THROW, 15000+rand()%15000);
				events.ScheduleEvent(EVENT_DEFLECTION, 15000);
			}
		}

		void AttackStart(Unit* who)
		{
			if (!_attackGUID || !_isActive)
				return;
			ScriptedAI::AttackStart(who);
		}

		void UpdateAI(uint32 diff)
		{
			if (!_isActive && !_attackGUID)
				return;

			events.Update(diff);
			uint32 eventId = events.GetEvent();

			if (eventId == EVENT_UNFREEZE)
			{
				events.PopEvent();
				me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
				me->CastSpell(me, SPELL_AWAKEN, true);
				me->RemoveAllAuras();
				_isActive = true;
				if (Unit* target = ObjectAccessor::GetUnit(*me, _attackGUID))
					AttackStart(target);
				return;
			}

			//Return since we have no target or if we are disabled from fight
			if (!UpdateVictim())
				return;

			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (eventId)
			{
				case EVENT_PIERCING_HOWL:
					me->CastSpell(me->GetVictim(), SPELL_PIERCING_HOWL, false);
					events.RepeatEvent(10000+rand()%1500);
					break;
				case EVENT_PENETRATING_STRIKE:
					me->CastSpell(me->GetVictim(), SPELL_PENETRATING_STRIKE, false);
					events.RepeatEvent(5000+rand()%5000);
					break;
				case EVENT_FRIGHTENING_SHOUT:
					me->CastSpell(me->GetVictim(), SPELL_FRIGHTENING_SHOUT, false);
					events.RepeatEvent(20000+rand()%8000);
					break;
				case EVENT_BLADE_TURNING:
					me->CastSpell(me->GetVictim(), me->GetMap()->IsHeroic() ? SPELL_BLADE_TURNING_H : SPELL_BLADE_TURNING_N, false);
					events.RepeatEvent(12000);
					break;
				case EVENT_THROW:
					me->CastSpell(SelectTarget(SELECT_TARGET_RANDOM,0,50.0f, true,0), me->GetMap()->IsHeroic() ? SPELL_THROW_H : SPELL_THROW_N, true);
					events.RepeatEvent(10000+rand()%15000);
					break;
				case EVENT_DEADLY_THROW:
					me->CastSpell(SelectTarget(SELECT_TARGET_RANDOM,0,50.0f, true,0), me->GetMap()->IsHeroic() ? SPELL_DEADLY_THROW_H : SPELL_DEADLY_THROW_N, true);
					events.RepeatEvent(15000+rand()%15000);
					break;
				case EVENT_DEFLECTION:
					me->CastSpell(me, me->GetMap()->IsHeroic() ? SPELL_DEFLECTION_H : SPELL_DEFLECTION_N, false);
					events.RepeatEvent(15000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

void AddSC_boss_volkhan()
{
	new boss_volkhan();
	new npc_molten_golem();
	new npc_hol_monument();
}
